/************************************************************************
This file is part of Polar4linux
Copyright (C) 2010-2011 Eric jrdn2
Portions Copyright (C) 2007 Matti Juvonen <mpj@iki.fi> (RS400 Tools)
Portions Copyright (C) 2006 Jani Hursti <jani.hursti@hut.fi>,
Portions Copyright (C) 2001-2003 Tom Oliveira e Silva

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
************************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "RS400_parse.h"

// Find minimum HRs for all laps. 
void find_lap_hr_min(rs400_session &session) {
    unsigned int lap,i = 0;
    for ( lap = 0; lap < session.number_of_laps; lap++) {
        session.lap[lap].hr_min = 255; // Set to maximum
        for (; (i * session.recording_interval < session.lap[lap].lap_end_time) && (i < session.number_of_samples); i++) {
            if (session.sample[i].hr < session.lap[lap].hr_min) {
                session.lap[lap].hr_min = session.sample[i].hr;
            }
        }
    }
}

// Work out the total distance for each lap. 
void find_lap_distances(rs400_session &session) {
    unsigned int lap,i = 0;
    for (lap = 0; lap < session.number_of_laps; lap++) {
        int distance = 0;
	if (session.has_pace_data)
	{
        for (; (i * session.recording_interval < session.lap[lap].lap_end_time) && (i < session.number_of_samples); i++) {
            distance += session.sample[i].speed * session.recording_interval;
        }
	}
        session.lap[lap].distance = distance / 36.0;
    }
}

// Work out the pace statistics.
void find_lap_paces(rs400_session &session) {
    unsigned int lap,i = 0;
    for (lap = 0; lap < session.number_of_laps; lap++) {
        int pace_end = 0;
        int pace_sum = 0;
        int samples = 0;
	if (session.has_pace_data)
	{
        for (; (i * session.recording_interval < session.lap[lap].lap_end_time) && (i < session.number_of_samples); i++) {
            pace_end = session.sample[i].speed;
            pace_sum += session.sample[i].speed;
            samples++;
        }
	}
        session.lap[lap].pace_end = pace_end;
        session.lap[lap].pace_avg = (int) round((double) pace_sum / samples);
    }
}


void RS400_parse_overview(const unsigned char data[], rs400_overview &rs400)
{
  rs400.number_of_sessions = data[4];
  unsigned int nbDATA=(data[7]<<16)+(data[8]<<8)+data[9];
  unsigned int totalMem=(data[17]<<16)+(data[18]<<8)+data[19]; //is it sure???
  rs400.pcFreeMem=100*(totalMem-nbDATA)/totalMem;
  printf("Found %d sessions\n", rs400.number_of_sessions);
  printf(" %d %% Free memory \n", rs400.pcFreeMem);
}

void RS400_parse_session_header(const unsigned char data[], rs400_session &session)
{
	//1) zero session
	session.reset();

	/* Default to SI units for now. TODO. */
    session.si_units = 1;

    /* Exercise name at most 8 characters */
    rs400_decode_string(session.name, &data[8]);
    /* Exercise type */
    session.has_pace_data = (data[21] & 0x8) >> 3;     /* Shift may be unnecessary... */
    
    switch (data[22]) {
        case 0:
            session.recording_interval = 1;
            break;
        case 1:
            session.recording_interval = 5;
            break;
        case 2:
            session.recording_interval = 10;
            break;
        case 3:
            session.recording_interval = 60;
            break;
//ej
		case 4:
            session.recording_interval = 1;
            break;

		case 5:
            session.recording_interval = 5;
            break;

		case 6:
            session.recording_interval = 15;
            break;

		case 7:
            session.recording_interval = 60;
            break;
    }



    unsigned short timestamp = toshort(&data[24]);
    session.day = timestamp & 0x1f;
    session.month = (timestamp & 0x1e0) >> 5;
    session.year = 2000 + (timestamp >> 9);
    /* Time is encoded as BCD. Can't you guys decide? */
    session.start_second = bcd2i(data[26]);
    session.start_minute = bcd2i(data[27]);
    session.start_hour = bcd2i(data[28]);
    /* Create a unique session id from the name and timestamp */
    sprintf(session.id, "%d-%02d-%02d_%02d:%02d:%02d",
            session.year,
            session.month,
            session.day,
            session.start_hour,
            session.start_minute,
            session.start_second//,
            //session.name
    );
    /* Session statistics */
    /* Session duration will be stored in seconds */
    session.duration = short2sec(&data[29]);

}

void RS400_parse_session(unsigned char data[], rs400_session & session)
{
  session.lap.clear();
  session.sample.clear();
	RS400_parse_session_header(data, session);
	
	/* Session heart rates */
	session.hr_avg = data[32];
	session.hr_max = data[33];
	session.hr_min = data[34];
	session.calories = toshort(&data[35]);

	/* User values */
	session.user_weight = data[37];
	session.user_hr_max = data[38];
	session.user_hr_rest = data[39];
	session.user_vo2_max = data[40];
	
	/* Pace and lap summary */
	if (session.has_pace_data)
	{
		session.distance = toshort(&data[44]) * 10;
	}
	
	session.number_of_phases = data[46];   
	session.total_number_of_intervals = data[47];
	session.number_of_laps = data[49];
	session.best_laptime = bcd_hmsc2sec(&data[50]);
	session.best_lap = data[54];
	session.pace_max = toshort(&data[55]);
	session.pace_avg = toshort(&data[57]);
	
	/* Find out how much storage is used for each lap entry */
	int sample_offset, lap_offset;
	if (session.has_pace_data)
	{
		sample_offset = 4;
		lap_offset = 13;
	}
	else
	{
		sample_offset = 1;
		lap_offset = 6;
	}

	session.number_of_samples = ceil(session.duration / session.recording_interval);
	
	const unsigned int nbPhaseSegments=ceil(session.total_number_of_intervals*12/256.0);
	unsigned int remainingPhaseSegments=nbPhaseSegments;
	
	//parse hr
	unsigned char *sample_data = &data[127];
	unsigned char *srcptr = sample_data;

	rs400_sample sample0;
	for(unsigned int i = 0; i < session.number_of_samples; i++)
	{
		//skip phases segments and padding values (every 256 values):
		if ( (i & 0xff) == 0x00 && i!=0 && remainingPhaseSegments>=1) {srcptr+=256;remainingPhaseSegments--;}
		if ( (i & 0xff) == 0x00) srcptr+=sample_offset;
		else
		{
			srcptr+=sample_offset;
			sample0.hr = *srcptr;
			if (session.has_pace_data) sample0.speed = toshort(&srcptr[2]);
			session.sample.push_back(sample0);
		}
	}
	
	// Start parsing laps, if any. The laps are stored at the end of the data.
	// Lap data starts at the beginning of the next 256-byte block
	unsigned char *lap_data = srcptr;
	while (((int)(lap_data - data) & 0xff) != 0x80) lap_data++;
	
	//here, we are at the first phase name, one has to jump after all phases descriptions
	lap_data+=session.number_of_phases*24;

  for (int lap = session.number_of_laps - 1; lap >= 0; lap--, lap_data += lap_offset)
	{
		rs400_lap current_lap;
		current_lap.lap_no = lap + 1;
		current_lap.lap_end_time = short2sec(&lap_data[0]);

		/* Calculate lap time */
		//lap times are not saved in the hrm file, so laptime computation is useless here
		//current_lap.laptime = current_lap.lap_end_time;
		
		current_lap.autolap = (lap_data[2] & 0x80);
		current_lap.hr_end = (lap_data[3]);
		current_lap.hr_avg = (lap_data[4]);
		current_lap.hr_max = (lap_data[5]);
		current_lap.laptype=0;
		session.lap.insert(session.lap.begin(),current_lap);
	}


	//parse intermediate times, if any
	for (unsigned int i=0;i<session.total_number_of_intervals;i++)
	{
		unsigned int segment=i/21;
		unsigned int positionInSegment=i%21;
		unsigned int shift=(segment==0)?5:2;
		unsigned char *wptr=sample_data+256+512*segment+positionInSegment*12+shift;
		rs400_lap current_lap;
		current_lap.lap_no = i + 1;
		current_lap.lap_end_time = short2sec(&wptr[0]);
		//current_lap.laptime = current_lap.lap_end_time;
		current_lap.autolap = 0;
		
		current_lap.hr_min = wptr[3];
		current_lap.hr_end = wptr[4];
		current_lap.hr_avg = wptr[5];
		current_lap.hr_max = wptr[6];
		current_lap.laptype=33554432;
		session.lap.insert(session.lap.begin(),current_lap);
	}

	//sort laps by end_time values;
	sort (session.lap.begin(), session.lap.end());
	
  /* Finally, generate some other values from the data */
  find_lap_hr_min(session);
  find_lap_distances(session);
  find_lap_paces(session);
	compute_laptimes(session);
}

